<!DOCTYPE HTML>
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <meta name="author" content="Caio Augusto Silva - BIPES Project">
  <meta name="description" content="EasyMQTT dashboard for data visualization">
  <title>BIPES EasyMQTT</title>
  <style>
  body{
    background-color: #1f2b38;
    font-family: sans-serif;
    margin:0;
  }

  .easymqtt-content{
    padding: 1ex;
  }

  .card, .highcharts-data-table{
    margin: 10px;
    box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2);
    background-color: #fff;
  }

  .card:hover, .highcharts-data-table:hover{
    box-shadow: 0 8px 16px 0 rgba(0,0,0,0.2);
    transition: 0.3s;
  }

  .highcharts-data-table{
    max-height: 400px;
    overflow-y: scroll;
  }

  .easymqtt-charts{
    display: flex;
    flex-direction: column;
    flex-wrap: wrap;
    justify-content: center;
  }
  @media only screen and (min-width: 768px) {
    .easymqtt-charts{
      flex-direction: row;
    }
  }

  .easymqtt-config{
    display: flex;
    flex-direction: row;
    font-size:14px;
    flex-wrap: wrap;
    justify-content: space-evenly;
  }

  .easymqtt-config button{
    font-size:14px;
  }

  .easymqtt-config div{
    margin: 10px;
  }

  .easymqtt-chart{
    flex: 1;
    min-width: 280px;
  }

  @media only screen and (min-width: 768px) {
    .easymqtt-chart{
      min-width: 500px;
    }
  }
  .easymqtt-warning{
    text-align: center;
    font-size: 18px;
    font-weight: bold;
    padding: 20px;
  }
  .easymqtt-warning span{
    font-size: 12px;
    font-weight: 400;
  }

  </style>
  <script src="jquery-3.5.1.min.js"></script>
  <script src="https://code.highcharts.com/stock/highstock.js"></script>
  <script src="https://code.highcharts.com/stock/modules/exporting.js"></script>
  <script src="https://code.highcharts.com/stock/modules/export-data.js"></script>
</head>
<body>
  <div id="easymqtt-content" class="content easymqtt-content">
    <div id="easymqtt-warning" class="easymqtt-warning card"></div>
    <div id="easymqtt-charts" class="easymqtt-charts"></div>
    <div id="easymqtt-config" class="easymqtt-config card" style="display: none;">
      <div id="easymqtt-config-publish">
        <label for="easymqtt-publish-value">Publish value:</label>
        <input type="number" id="easymqtt-publish-value" name="easymqtt-publish-value" step="0.1" value="0" style="width: 40px;">
        <label for="easymqtt-publish-topic">to topic:</label>
        <input type="text" id="easymqtt-publish-topic" name="easymqtt-publish-topic" style="width: 100px;">
        <button id="easymqtt-publish-submit" type="button" onclick="publishEasyMQTT($('#easymqtt-publish-topic').val(),$('#easymqtt-publish-value').val())">Submit</button>
      </div>
      <div id="easymqtt-config-update">
        <label for="easymqtt-update-freq">Update frequency (s):</label>
        <input type="number" id="easymqtt-update-freq" name="easymqtt-update-freq" min="1" step="1" value="5" style="width: 40px;">
        <button id="easymqtt-update-freq-set" type="button" onclick="setEasyMQTTUpdate($('#easymqtt-update-freq').val())">Set</button>
      </div>
      <div id="easymqtt-config-clear">
        <button id="easymqtt-clear-data" type="button" onclick="clearEasyMQTTSession()">Clear Data</button>
      </div>
    </div>
  </div>
  <script type="text/javascript">
    var timerUpdateEasyMQTT;
    var MQTTCharts = {};
    var easyMQTT_session_chart;

    function getSessionFromURL(){
      let searchParams = new URLSearchParams(window.location.search);
      if (searchParams.has('session')){
        return searchParams.get('session');
      }
      return null;
    }

    // Init
    let urlSession = getSessionFromURL();
    if (urlSession){
      startEasyMQTT(urlSession);
    }else{
      if (parent === window){
        errorEasyMQTT("EasyMQTT Session ID is not currently defined!<br><span>Try passing it as parameter in URL (?session=<session_id>)</span>");
      }else{
          errorEasyMQTT("EasyMQTT Session ID is not currently defined!<br><span>If the EasyMQTT Start block is set try clicking on 'Python' tab first.</span>");
      }
    }

    function startEasyMQTT(session){ 
      if (typeof session === 'undefined'){
        errorEasyMQTT("EasyMQTT Session ID is not currently defined!<br><span>If the EasyMQTT Start block is set try clicking on 'Python' tab first.</span>");
        if (typeof timerUpdateEasyMQTT !== 'undefined')
          cancelTimer(timerUpdateEasyMQTT);
      }else{
        if (easyMQTT_session_chart == session && typeof timerUpdateEasyMQTT !== 'undefined'){
          return;
        }
        if (typeof easyMQTT_session_chart !== 'undefined' && easyMQTT_session_chart != session){
          clearInterval(timerUpdateEasyMQTT);
          resetEasyMQTTCharts();
        } 
        easyMQTT_session_chart = session;
        $("#easymqtt-config").show();
        setEasyMQTTUpdate($('#easymqtt-update-freq').val());
      }
    }

    function resetEasyMQTTCharts(){
      for (topic in MQTTCharts){
        MQTTCharts[topic].destroy();
        delete MQTTCharts[topic];
        $('#easymqtt-'+topic).remove();
      }
    }

    function setEasyMQTTUpdate(interval){
      if (typeof interval === 'undefined' || interval < 0){
        alert("Invalid update interval");
        return;
      }
      clearInterval(timerUpdateEasyMQTT);
      updateEasyMQTTJson();
      timerUpdateEasyMQTT = setInterval(updateEasyMQTTJson,interval*1000);
    }

    function dismissEasyMQTTError(){
      $('#easymqtt-warning').html("");
      $('#easymqtt-warning').hide();
    }

    function errorEasyMQTT(error){
      $('#easymqtt-warning').html(error);
      $('#easymqtt-warning').show();

    }

    function clearEasyMQTTSession(){
      if (confirm("Are you sure you want to clear all data in this topic?\nThis action can not be reverted") == true){
        $.get( `https://bipes.net.br/easymqtt/clearsession.php`,
          {"session": easyMQTT_session_chart}, 
          function( data ) {
            if (data.success){
              resetEasyMQTTCharts();
              errorEasyMQTT(data.result);
            }else{
              errorEasyMQTT(data.result);
            }
          }, "json")
        .fail(function(xhr, status, error){
            errorEasyMQTT("Error clearing session. " + status + " " + error);
          }
        );
      }
    }

    function publishEasyMQTT(topic,value){
      if (typeof topic === 'undefined' || typeof value === 'undefined' || topic.length < 1){
        alert("Invalid input");
        return;
      }
      $.get( `https://bipes.net.br/easymqtt/publish.php`,
        {"session": easyMQTT_session_chart,
         "topic": topic,
         "value": value
        }, 
        function( data ) {
          errorEasyMQTT(data.result);
        }, "json")
      .fail(function(xhr, status, error){
          errorEasyMQTT("Error publising data. " + status + " " + error);
        }
      );
    }

    function addNewChart(topic){
      MQTTCharts[topic] = new Highcharts.stockChart({
        chart: {
          renderTo: 'easymqtt-'+topic,
        },
        time: {
          useUTC: false
        },
        title: {
          text: 'Live data from ' + topic
        },
        rangeSelector: {
          buttons: [{
              count: 1,
              type: 'minute',
              text: '1M'
          }, {
              count: 5,
              type: 'minute',
              text: '5M'
          }, {
              count: 1,
              type: 'hour',
              text: '1H'
          }, {
              count: 12,
              type: 'hour',
              text: '12H'
          }, {
              type: 'all',
              text: 'All'
          }],
          inputEnabled: false,
          selected: 4
        },
        exporting: {
            enabled: true
        },
        xAxis: {
          type: 'datetime',
        },
        yAxis: {
          title: {
            text: 'Value'
          }
        },
        series: [{
          name: topic,
          data: [],
          marker: {
            enabled: true,
            radius: 2
        },
        }]
      });
    }

    function updateEasyMQTTTopic(topic,since=0){
      $.get( `https://bipes.net.br/easymqtt/gettopic.php`,
        {"session": easyMQTT_session_chart, "topic": topic, "since": since}, 
        function( data ) {
          if (data.success){
            if (data.result.length > 0){
              if (!(topic in MQTTCharts)){
                $("#easymqtt-charts").append('<div id="easymqtt-'+topic+'" class="easymqtt-chart card"></div>')
                addNewChart(topic);
                for (i in MQTTCharts){
                  MQTTCharts[i].reflow();
                }
              }
              for (i in data.result){
                MQTTCharts[topic].series[0].addPoint([data.result[i].timestamp*1000,parseFloat(data.result[i].data)],false);
              }
              MQTTCharts[topic].redraw();
            }
          }else{
            errorEasyMQTT(data.result);
          }
        }, "json")
      .fail(function(xhr, status, error){
          errorEasyMQTT(topic + " request error. " + status + " " + error);
        }
      );
    }
    function updateEasyMQTTJson(){
      //$(".highcharts-navigator").removeAttr("visibility"); //needed only if not used as an iframe in the main page
      $.get( `https://bipes.net.br/easymqtt/getsession.php`,
      {"session": easyMQTT_session_chart}, 
      function( data ) {
        if (data.success){
          dismissEasyMQTTError();
          for (i in data.result){
            if (data.result[i] in MQTTCharts){
              let last_update = MQTTCharts[data.result[i]].series[0].points[MQTTCharts[data.result[i]].series[0].points.length-1].x / 1000;
              updateEasyMQTTTopic(data.result[i],last_update + 1);
            }else{
              updateEasyMQTTTopic(data.result[i]);
            }
          }
        }else{
          errorEasyMQTT(data.result);
        }
      }, "json" )
      .fail(function(xhr, status, error){
          errorEasyMQTT("Session" + session + " request error. " + status + " " + error);
        }
      );
    }
  </script>
</body>
</html>
